/*

Copyright (C) 2000 - 2008 Christian Kreibich <christian@whoop.org>.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies of the Software and its documentation and acknowledgment shall be
given in the documentation and software packages that this Software was
used.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/

#include <nd.h>
#include <nd_gui.h>
#include <nd_protocol.h>
#include <nd_raw_protocol.h>
#include <nd_protocol_inst.h>

#include <netdb.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <time.h>

#include <nd_ip.h>
#include <nd_ip_callbacks.h>
#include "support.h"


#define ND_IP_GTK_BUILDER_FILE "ip.xml"


/* global list of gtk objects */
GtkBuilder				 *nd_ip_builder = NULL;
GtkWindow				 *nd_ip_frag_dialog = NULL;

/* Definition of all standard IPv4 header fields */

static ND_ProtoField ip_fields[] = {
  { ND_VAL_FIELD, N_("Vers. (%u)"),        N_("IP protocol version"),              4,  nd_ip_v_cb   },
  { ND_VAL_FIELD, N_("Header len. (%u)"),  N_("Header length in 32-bit words"),    4,  nd_ip_hl_cb  },
  { ND_VAL_FIELD, N_("(%s)"),              N_("Explicit Congestion Notification"), 2,  nd_ip_ecn_cb },
  { ND_VAL_FIELD, N_("ToS (%s)"),          N_("Type of service"),                  6,  nd_ip_tos_cb },
  { ND_VAL_FIELD, N_("Length (%u)"),       N_("Total length in bytes"),            16, nd_ip_len_cb },
  { ND_VAL_FIELD, N_("ID (%u)"),           N_("Identification"),                   16, nd_ip_id_cb  },
  { ND_FLG_FIELD, N_("R"),                 N_("Reserved bit"),                     1,  nd_ip_rf_cb  },
  { ND_FLG_FIELD, N_("D"),                 N_("Don't fragment bit"),               1,  nd_ip_df_cb  },
  { ND_FLG_FIELD, N_("M"),                 N_("More fragments bit"),               1,  nd_ip_mf_cb  },
  { ND_VAL_FIELD, N_("Frag. Offset (%u)"), N_("Fragment offset"),                  13, nd_ip_off_cb },
  { ND_VAL_FIELD, N_("TTL (%u)"),          N_("Time to live"),                     8,  nd_ip_ttl_cb },
  { ND_VAL_FIELD, N_("Protocol (%s)"),     N_("Protocol contained"),               8,  nd_ip_p_cb   },
  { ND_VAL_FIELD, N_("Checksum (0x%.4x)"), N_("Checksum"),                         16, nd_ip_sum_cb },
  { ND_VAL_FIELD, N_("Src. addr. (%s)"),   N_("Source address"),                   32, nd_ip_src_cb },
  { ND_VAL_FIELD, N_("Dst. addr. (%s)"),   N_("Destination address"),              32, nd_ip_dst_cb },
  { 0, NULL, NULL, 0, NULL }
};


/* Field definitions for IPv4 options: */

static ND_ProtoField ip_opt_fields[] = {
  { ND_VAL_FIELD, N_("Opt. (%s)"),        N_("IP option type"),             8,  nd_proto_8bit_cb     },
  { ND_VAL_FIELD, N_("Len. (%u)"),        N_("IP option length"),           8,  nd_proto_8bit_cb     },
  { ND_VAL_FIELD, N_("Sec. (0x%.4x)"),    N_("Security specification"),     16, nd_proto_16bit_cb    },
  { ND_VAL_FIELD, N_("Comp. (0x%.4x)"),   N_("Compartments"),               16, nd_proto_16bit_cb    },
  { ND_VAL_FIELD, N_("HR (0x%.4x)"),      N_("Handling restrictions"),      16, nd_proto_16bit_cb    },
  { ND_VAL_FIELD, N_("TCC (0x%.6x)"),     N_("Transmission Control Code"),  24, nd_proto_24bit_cb    },
  { ND_VAL_FIELD, N_("Off. (%u)"),        N_("Route data pointer"),         8,  nd_proto_8bit_cb     },
  { ND_VAL_FIELD, N_("%s"),               N_("Routing IP address"),         32, nd_ip_opt_addr_cb          },
  { ND_VAL_FIELD, N_("SID (0x%.4x)"),     N_("Stream ID"),                  16, nd_proto_16bit_cb    },
  { ND_VAL_FIELD, N_("Oflw (%u)"),        N_("Overflows"),                  4,  nd_proto_4bit_hi_cb  },
  { ND_VAL_FIELD, N_("Flg (%u)"),         N_("Timestamp specification"),    4,  nd_proto_4bit_lo_cb  },
  { ND_VAL_FIELD, N_("%s"),               N_("Timestamp value"),            32, nd_proto_32bit_cb    },
  { ND_VAL_FIELD, N_("(%u bytes data)"),  N_("Unknown option data"),        0,  NULL,                      },
  { ND_VAL_FIELD, N_("0x%.4x"),           N_("Router alert value"),         16, nd_proto_16bit_cb,   },
  { 0, NULL, NULL, 0, NULL}    
};

/* Menu definitions: */

ND_MenuData ip_menu_p_data[] = {
  { N_("TCP"),       N_("TCP"),                                    IPPROTO_TCP,  nd_ip_p_value_cb },
  { N_("UDP"),       N_("UDP"),                                    IPPROTO_UDP,  nd_ip_p_value_cb },
  { N_("ICMP"),      N_("ICMP"),                                   IPPROTO_ICMP, nd_ip_p_value_cb },
  { N_("IGMP"),      N_("IGMP"),                                   IPPROTO_IGMP, nd_ip_p_value_cb },
  { N_("IPIP"),      N_("IP in IP Tunnel"),                        IPPROTO_IPIP, nd_ip_p_value_cb },
  { N_("EGP"),       N_("EGP"),                                    IPPROTO_EGP,  nd_ip_p_value_cb },
  { N_("RSVP"),      N_("RSVP"),                                   IPPROTO_RSVP, nd_ip_p_value_cb },
  { N_("IPSec ESP"), N_("IPSec Encapsulating Security Payload"),   IPPROTO_ESP,  nd_ip_p_value_cb },
  { N_("IPSec AH"),  N_("IPSec Authentication Header"),            IPPROTO_AH,   nd_ip_p_value_cb },
  { N_("Raw IP"),    N_("Raw IP"),                                 IPPROTO_RAW,  nd_ip_p_value_cb },
  { N_("Custom"),    N_("Custom protocol value"),                  -1,           nd_ip_p_custom_cb },
  { NULL, NULL, 0, NULL}
};


ND_MenuData ip_menu_tos_data[] = {
  { N_("Low delay"),         N_("Minimize delay"),       IPTOS_LOWDELAY,    nd_ip_tos_value_cb },
  { N_("Throughput"),        N_("Maximize throughput"),  IPTOS_THROUGHPUT,  nd_ip_tos_value_cb },
  { N_("Reliability"),       N_("Maximize reliability"), IPTOS_RELIABILITY, nd_ip_tos_value_cb },
  { N_("Low cost"),          N_("Minimize cost"),        IPTOS_LOWCOST,     nd_ip_tos_value_cb },
  { N_("None"),              N_("Clear all bits"),       0,                 nd_ip_tos_value_cb },
  { N_("Custom"),            N_("Custom ToS value"),     -1,                nd_ip_tos_custom_cb },
  { NULL, NULL, 0, NULL}
};


ND_MenuData ip_menu_ecn_data[] = {
  { N_("-"),                 N_("Not ECN-capable transport"),     0x00,     nd_ip_ecn_value_cb },
  { N_("ECT(0)"),            N_("Sender ECN-capable"),            0x01,     nd_ip_ecn_value_cb },
  { N_("ECT(1)"),            N_("Receiver ECN-capable"),          0x02,     nd_ip_ecn_value_cb },
  { N_("CE"),                N_("Congestion experienced"),        0x03,     nd_ip_ecn_value_cb },
  { NULL, NULL, 0, NULL}
};


ND_MenuData ip_menu_data[] = {
  { N_("Fragment Packet ..."), N_("Fragments the IP packet in two fragments"), 0, nd_ip_fragment_cb },
  { N_("Reassemble Packets"),  N_("Reassembles an IP packet"),                 0, nd_ip_reassemble_cb },
  { N_("Fix Checksums"),       N_("Recalculates the IP checksum"),             0, nd_ip_cksum_fix_cb },
  { NULL, NULL, 0, NULL}
};


static LND_Protocol *ip;
static ND_Protocol  *ip_gui;


/* Plugin hook implementations: ---------------------------------------- */

const char *
name(void)
{
  return ("IPv4 Plugin");
}


const char *
description(void)
{
  return ("A plugin providing IPv4 support, including:\n"
	  " - ECN codepoints according to RFC 3168\n"
	  " - IP options according to RFCs 791 and 2113\n"
	  " - Fragmentation and Reassembly");
}


const char *
author(void)
{
  return ("Christian Kreibich, <christian@whoop.org>");
}


const char *
version(void)
{
  return VERSION_MAJOR;
}


LND_Protocol *
init(void)
{
	GError *err = NULL;
	unsigned res = 0;
	
	if (! (ip = libnd_proto_registry_find(LND_PROTO_LAYER_NET, ETHERTYPE_IP)))
		return NULL;
	
	ip_gui = nd_proto_new(ip);
	
	ip_gui->create_gui      = nd_ip_create_gui;
	ip_gui->set_gui         = nd_ip_set_gui;
	
	/* We're using a button table to display the protocol content,
		so we need to hook it in here: */
	ip_gui->fields = ip_fields;
	ip_gui->header_width = 32;
	
	/* IP isn't stateful, so we don't need any of the state-related
		functions. */
	
	/* We provide a little menu that allows us to fragment/defragment
		packets, and fix checksums. */
	ip_gui->menu = nd_gui_create_menu(ip_menu_data);
	
	/* Get GTKBuilder interface */
	nd_ip_builder = gtk_builder_new ();
	if (nd_ip_builder == NULL) {
		D(("Failed to create GTK builder!\n"));
		return 1;
	}
	res = gtk_builder_add_from_file (nd_ip_builder, ND_IP_GTK_BUILDER_FILE, &err);
	if (res == 0) {
		D(("%s\n", err->message));
		g_error_free(err);
		return 2;
	}
	
	/*
	 * Frequently used pointers should be conveniently global as well.
	 */
	/* fragment packet dialog */
	ND_GTK_GET_TYPE(GTK_WINDOW, nd_ip_frag_dialog, nd_ip_builder, "ip_frag_dialog");
	
	/* Connect signals */
	gtk_builder_connect_signals(nd_ip_builder, NULL);
	
	return ip;
}


/* Protocol method implementations: ------------------------------------ */

GtkWidget *
nd_ip_create_gui(LND_Trace *trace, LND_ProtoInfo *pinf)
{
  GtkWidget *table;

  table = nd_gui_proto_table_create(trace, pinf);
  return table;
}


void       
nd_ip_set_gui(const LND_Packet *packet, LND_ProtoInfo *pinf)
{
  struct ip   *iphdr;
  LND_ProtoData *pd;
  struct protoent *pe;

  iphdr = (struct ip*) libnd_packet_get_data(packet, ip, pinf->inst.nesting);
  if (!iphdr)
    return;

  D(("Setting IP GUI at nesting %i\n", pinf->inst.nesting));

  nd_ip_set_gui_v(pinf, iphdr);
  nd_ip_set_gui_hl(pinf, iphdr);
  nd_ip_set_gui_ecn(pinf, iphdr);
  nd_ip_set_gui_tos(pinf, iphdr);
  nd_ip_set_gui_len(pinf, iphdr);
  nd_ip_set_gui_id(pinf, iphdr);
  nd_ip_set_gui_rf(pinf, iphdr);
  nd_ip_set_gui_df(pinf, iphdr);
  nd_ip_set_gui_mf(pinf, iphdr);
  nd_ip_set_gui_off(pinf, iphdr);
  nd_ip_set_gui_ttl(pinf, iphdr);
  nd_ip_set_gui_p(pinf, iphdr);
  nd_ip_set_gui_sum(packet, pinf, iphdr);
  nd_ip_set_gui_src(pinf, iphdr);
  nd_ip_set_gui_dst(pinf, iphdr);
  nd_ip_set_gui_options(pinf, iphdr, packet);

  /* Try to label the next tab appropriately if it's not a handled
   * protocol (e.g., "HTTP" is nicer than saying "(remaining)").
   *
   * First check if there actually is uninterpreted data and
   * TCP coming before it, and if we can then find a string
   * representation of the port number, replace the label of
   * the raw data displayer with that service name.
   */
  if (! (pd = libnd_packet_get_last_nonraw(packet)) || !libnd_packet_get_trace(packet))
    return;
  
  if (pd->inst.proto != ip)
    return;

  if ( (pe = getprotobynumber(iphdr->ip_p)))
    {
      ND_ProtoInfo *pinf_raw = nd_raw_proto_get_gui(libnd_packet_get_trace(packet));
      gtk_label_set_text(GTK_LABEL(pinf_raw->proto_label), pe->p_name);
    }
}


  
/* Misc helper stuff below --------------------------------------------- */

void       
nd_ip_set_gui_v(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set(pinf, &ip_fields[0], DATA_TO_PTR(iphdr->ip_v));
}


void       
nd_ip_set_gui_hl(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set(pinf, &ip_fields[1], DATA_TO_PTR(iphdr->ip_hl));
}


void       
nd_ip_set_gui_ecn(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set_for_menu(pinf, &ip_fields[2], DATA_TO_PTR(iphdr->ip_tos >> 6),
			      ip_menu_ecn_data, "%u");

}


void       
nd_ip_set_gui_tos(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set_for_menu(pinf, &ip_fields[3], DATA_TO_PTR(iphdr->ip_tos),
			      ip_menu_tos_data, "0x%.2x");

}


void       
nd_ip_set_gui_len(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set(pinf, &ip_fields[4], DATA_TO_PTR(ntohs(iphdr->ip_len)));
}


void       
nd_ip_set_gui_id(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set(pinf, &ip_fields[5], DATA_TO_PTR(ntohs(iphdr->ip_id)));
}


void       
nd_ip_set_gui_rf(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set(pinf, &ip_fields[6], DATA_TO_PTR(ntohs(iphdr->ip_off) & IP_RF));
}


void       
nd_ip_set_gui_df(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set(pinf, &ip_fields[7], DATA_TO_PTR(ntohs(iphdr->ip_off) & IP_DF));
}


void       
nd_ip_set_gui_mf(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set(pinf, &ip_fields[8], DATA_TO_PTR(ntohs(iphdr->ip_off) & IP_MF));
}


void       
nd_ip_set_gui_off(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set(pinf, &ip_fields[9], DATA_TO_PTR(ND_IP_FRAG_OFFSET(iphdr)));
}


void       
nd_ip_set_gui_ttl(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set(pinf, &ip_fields[10], DATA_TO_PTR(iphdr->ip_ttl));
}


void       
nd_ip_set_gui_p(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set_for_menu(pinf, &ip_fields[11], DATA_TO_PTR(iphdr->ip_p),
			      ip_menu_p_data, "%i");
}


void       
nd_ip_set_gui_sum(const LND_Packet *packet, LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set(pinf, &ip_fields[12], DATA_TO_PTR(ntohs(iphdr->ip_sum)));
  
  /* Checksum highlighting -- if we're beyond the point in
   * the packet where it makes sense to check this, we 
   * show a "maybe", otherwise we check ourselves.
   */
  if (libnd_packet_get_proto_index(packet, &pinf->inst) >
      libnd_packet_get_last_fixable_proto(packet))
    {
      nd_proto_info_field_set_state(pinf, 
				    &ip_fields[12],
				    ND_FIELD_STATE_UNKN);
      return;
    }

  if (!libnd_ip_csum_correct(iphdr, NULL))
    nd_proto_info_field_set_state(pinf, 
				  &ip_fields[12],
				  ND_FIELD_STATE_ERROR);
  else
    nd_proto_info_field_set_state(pinf,
				  &ip_fields[12],
				  ND_FIELD_STATE_NORMAL);
}


void       
nd_ip_set_gui_src(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set(pinf, &ip_fields[13], inet_ntoa(iphdr->ip_src));
}


void       
nd_ip_set_gui_dst(LND_ProtoInfo *pinf, struct ip *iphdr)
{
  nd_proto_field_set(pinf, &ip_fields[14], inet_ntoa(iphdr->ip_dst));
}


void
nd_ip_set_gui_options(LND_ProtoInfo *pinf, struct ip *iphdr,
		      const LND_Packet *packet)
{
  int          opts_len, o_len, val, opts_done, opts_done_old;
  guchar      *opts_p, *o_next;
  gboolean     is_error;

  D_ASSERT_PTR(iphdr);
  if (!iphdr)
    return;
  
  nd_gui_proto_table_clear(libnd_packet_get_trace(packet), pinf);
  
  /* Setup options GUI elements manually: */
  
  opts_len  = (iphdr->ip_hl*4) - 20;
  opts_done = 0;
  opts_done_old = -1;
  
  if (opts_len == 0)
    return;

  while (opts_done < opts_len && opts_done != opts_done_old)
    {
      opts_done_old = opts_done;
      is_error = FALSE;
      opts_p = ((guchar *) iphdr) + 20 + opts_done;
  
      switch(*opts_p)
	{
	case 0:
	  /* end of option list - RFC791 */
	  
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[0], _("EOL"), FALSE);
	  opts_p++;
	  opts_done = opts_len;
	  break;
	  
	case 1:
	  /* No op */
	  
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[0], _("NOP"), FALSE);
	  opts_done++;
	  opts_p++;
	  break;
	  
	case 130:
	case 133:
	  /*Security - see RFC1108*/
	  opts_p++;
	  o_len = *opts_p;
	  opts_done += o_len;

	  if (opts_done > opts_len)
	    break;
	  
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[0], _("Sec"), is_error);
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[1], DATA_TO_PTR(o_len), is_error);
	  opts_p++;
	  
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[2], DATA_TO_PTR(ntohs(*((guint16 *) opts_p))), is_error);
	  opts_p += 2;
	  
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[3], DATA_TO_PTR(ntohs(*((guint16 *) opts_p))), is_error);
	  opts_p += 2;
	  
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[4], DATA_TO_PTR(ntohs(*((guint16 *) opts_p))), is_error);
	  opts_p += 2;
	  
#ifdef WORDS_BIGENDIAN
	  val = (opts_p[2] << 16 | opts_p[1] << 8 | opts_p[0]);
#else
	  val = (opts_p[0] << 16 | opts_p[1] << 8 | opts_p[2]);
#endif
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[5], DATA_TO_PTR(val), is_error);
	  opts_p += 3;
	  break;
	  
	case 131:
	  /*Loose Source and Record Route - RFC791*/
	  opts_p++;
	  o_len = *opts_p;
	  o_next = opts_p + o_len - 1;
	  opts_done += o_len;

	  if (opts_done > opts_len)
	    break;
	  
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[0], _("LS&RR"), is_error);
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[1], DATA_TO_PTR(o_len), is_error);
	  opts_p++;
	  
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[6], DATA_TO_PTR(*opts_p), is_error);
	  opts_p++;
	  
	  while (opts_p < o_next)
	    {
	      nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[7],
				     inet_ntoa(*((struct in_addr *)opts_p)), is_error);
	      opts_p += 4;
	    }
	  break;

	case 137:
	  /*Strict Source and Record Route - RFC791*/
	  opts_p++;
	  o_len = *opts_p;
	  o_next = opts_p + o_len - 1;
	  opts_done += o_len;

	  if (opts_done > opts_len)
	    break;

	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[0], _("SS&RR"), is_error);
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[1], DATA_TO_PTR(o_len), is_error);
	  opts_p++;

	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[6], DATA_TO_PTR(*opts_p), is_error);
	  opts_p++;

	  while (opts_p < o_next)
	    {
	      nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[7],
				     inet_ntoa(*((struct in_addr *)opts_p)), is_error);
	      opts_p += 4;
	    }
	  break;

	case 7:
	  /*Record Route - RFC791*/
	  opts_p++;
	  o_len = *opts_p;
	  o_next = opts_p + o_len - 1;
	  opts_done += o_len;

	  if (opts_done > opts_len)
	    break;

	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[0], _("RR"), is_error);
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[1], DATA_TO_PTR(o_len), is_error);
	  opts_p++;

	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[6], DATA_TO_PTR(*opts_p), is_error);
	  opts_p++;
	  
	  while (opts_p < o_next)
	    {
	      nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[7],
				     inet_ntoa(*((struct in_addr *)opts_p)), is_error);
	      opts_p += 4;
	    }
	  break;

	case 136:
	  /*Stream ID - RFC791*/
	  opts_p++;
	  o_len = *opts_p;
	  opts_done += o_len;

	  if (opts_done > opts_len)
	    break;

	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[0], _("SID"), is_error);
	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[1], DATA_TO_PTR(o_len), is_error);
	  opts_p++;

	  nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[8],
				 DATA_TO_PTR(ntohs(*((guint16 *) opts_p))), is_error);
	  opts_p++;
	  break;

	case 68:
	  {
	    int flg;

	    /*Internet timestamp - RFC791*/
	    opts_p++;
	    o_len = *opts_p;
	    o_next = opts_p + o_len - 1;
	    opts_done += o_len;
	    
	    if (opts_done > opts_len)
	      break;

	    nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[0], _("TS"), is_error);
	    nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[1], DATA_TO_PTR(o_len), is_error);
	    opts_p++;
		
	    nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[9],
				   DATA_TO_PTR((*opts_p & 0xF0) >> 4), is_error);
	    
	    flg = (*opts_p & 0x0F);
	    nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[10], DATA_TO_PTR(flg), is_error);
	    opts_p++;
		
	    while (opts_p < o_next)
	      {
		char *time_string;
		guint32 time_val;
		
		time_val = * (guint32*) opts_p;
		time_val = htonl(time_val);
		time_string = ctime((const time_t*) &time_val);
		time_string[strlen(time_string)-1] = '\0';

		if (flg != 0)
		  {
		    nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[7],
					   inet_ntoa(*((struct in_addr *)opts_p)), is_error);
		    opts_p += 4;
		  }

		nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[11], time_string, is_error);
		opts_p += 4;
	      }
	  }
	  break;

	case 148:
	  /* Router Alert - See RFC2113 */

	    opts_p++;
	    o_len = *opts_p;
	    opts_done += o_len;
	    
	    if (opts_done > opts_len)
	      break;

	    nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[0], _("RA"), is_error);
	    nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[1], DATA_TO_PTR(o_len), is_error);
	    opts_p++;

	    nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[13],
				   DATA_TO_PTR(ntohs(*((guint16 *) opts_p))), is_error);
	    opts_p += 2;
	    break;

	default:
	  {
	    char s[128];

	    g_snprintf(s, 128, "%i", *opts_p);

	    opts_p++;
	    o_len = *opts_p;
	    opts_done += o_len;
	    
	    if (opts_done > opts_len)
	      break;
	    
	    nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[0], s, is_error);
	    nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[1], DATA_TO_PTR(o_len), is_error);
	    opts_p++;
	    
	    if (o_len > 2)
	      nd_gui_proto_table_add(libnd_packet_get_trace(packet), pinf, &ip_opt_fields[12], DATA_TO_PTR(o_len - 2), is_error);
	    
	    opts_p += o_len;
	  }
	}

      if (opts_done > opts_len)
	D(("Warning -- bogus options. ...\n"));
    }	  
}


inline LND_Protocol *
nd_ip_get(void)
{
  return ip;
}


inline ND_Protocol *
nd_ip_get_gui(void)
{
  return ip_gui;
}


